package com.gugugu.web.rpc.server;

import com.guguguweb.rpc.codec.MessageDecoder;
import com.guguguweb.rpc.codec.MessageEncoder;
import com.guguguweb.rpc.net.server.AbstractRpcServer;
import com.guguguweb.rpc.net.server.Handler.LocalRequestHandler;
import com.guguguweb.rpc.net.server.Handler.NettyServerHandler;
import com.guguguweb.rpc.provider.LocalServiceProviderImpl;
import com.guguguweb.rpc.register.imp.NacosServiceRegistry;
import com.guguguweb.rpc.serializer.CommonSerializer;
import com.guguguweb.rpc.util.NacosUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyRpcServerForSpringBoot extends AbstractRpcServer {
    private final CommonSerializer serializer;

    public NettyRpcServerForSpringBoot(){
        this("localhost",8848,3371);
    }


    public NettyRpcServerForSpringBoot(String nacosHost, int nacosPort,int serverPort) {
        this(nacosHost, nacosPort,serverPort ,DEFAULT_SERIALIZER);
    }



    public NettyRpcServerForSpringBoot(String nacosHost, int nacosPort, int serverPort,Integer serializerCode) {
        this.nacosHost = nacosHost;
        this.nacosPort = nacosPort;
        this.serverPort = serverPort;
        NacosUtil nacosUtil = new NacosUtil(nacosHost+":"+nacosPort);
        serviceRegistry = new NacosServiceRegistry(nacosUtil);
        serviceProvider = new LocalServiceProviderImpl();
        serializer = CommonSerializer.getByCode(serializerCode);
    }

    @Override
    public void start() {
        long start = System.currentTimeMillis();
        log.info("GuRpc服务端启动开始");
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //用于连接后处理IO事件的从”线程池“
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .option(ChannelOption.SO_BACKLOG, 5000)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //配置Channel参数，nodelay没有延迟，true就代表禁用Nagle算法，减小传输延迟。理解可参考：https://blog.csdn.net/lclwjl/article/details/80154565
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //初始化管道
                            ChannelPipeline pipeline = ch.pipeline();
                            //往管道中添加Handler，注意入站Handler与出站Handler都必须按实际执行顺序添加，比如先解码再Server处理，那Decoder()就要放在前面
                            //但入站和出站Handler之间则互不影响，这里我就是先添加的出站Handler再添加的入站
                            //设定IdleStateHandler心跳检测每30秒进行一次读检测，如果30秒内ChannelRead()方法未被调用则触发一次userEventTrigger()方法
                            pipeline
                                    //.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS))
                                    .addLast(new MessageEncoder(serializer))
                                    .addLast(new MessageDecoder())
                                    .addLast(new NettyServerHandler(new LocalRequestHandler(serviceProvider)));
                        }
                    });
            ChannelFuture future = serverBootstrap.bind(serverPort).sync();
            log.info("GuRpc服务端启动成功,启动耗时： {} ms",System.currentTimeMillis() - start);
            future.channel().closeFuture().sync();
        }catch (Exception e){
            log.error("启动服务器时有错误发生", e);
        }finally {
            //优雅关闭Netty服务端且清理掉内存，shutdownGracefully()执行逻辑参考：https://www.icode9.com/content-4-797057.html
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
